Mostrar/Ocultar Código
import yfinance as yf
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler# Tickers da carteira
tickers = ['BRFS3.SA', 'JBSS3.SA', 'BEEF3.SA', 'MRFG3.SA', 'TSN', 'HRL', 'GIS']
# Download dos preços históricos
dados = yf.download(tickers, start="2024-10-01", end="2025-04-25", progress=False)
# Verificação dos dados baixados
if isinstance(dados.columns, pd.MultiIndex):
downloaded_tickers = dados.columns.levels[1].tolist()
available_tickers = [ticker for ticker in downloaded_tickers if not dados[('Close', ticker)].isnull().all()]
else:
if not dados.empty:
available_tickers = [ticker for ticker in tickers if not dados[ticker].isnull().all()]
else:
available_tickers = []
if not available_tickers:
raise ValueError("❌ Nenhum dado foi baixado. Verifique os tickers e o período.")
print(f"✅ Dados baixados para: {', '.join(available_tickers)}")C:\Users\kuiav\AppData\Local\Temp\ipykernel_36060\1853906305.py:5: FutureWarning:
YF.download() has changed argument auto_adjust default to True
✅ Dados baixados para: BEEF3.SA, BRFS3.SA, GIS, HRL, JBSS3.SA, MRFG3.SA, TSN
precos = pd.DataFrame()
for ticker in available_tickers:
if ('Adj Close', ticker) in dados.columns and (not dados[('Adj Close', ticker)].isnull().all()):
precos[ticker] = dados[('Adj Close', ticker)]
elif ('Close', ticker) in dados.columns and (not dados[('Close', ticker)].isnull().all()):
print(f"⚠️ Usando 'Close' para {ticker}, pois 'Adj Close' não está disponível.")
precos[ticker] = dados[('Close', ticker)]
else:
print(f"❌ Dados insuficientes para {ticker}. Ignorando.")
if precos.empty:
raise ValueError("❌ Nenhuma coluna válida encontrada para análise.")
precos = precos.dropna()
if precos.empty:
raise ValueError("❌ Dados insuficientes após remoção de valores nulos.")⚠️ Usando 'Close' para BEEF3.SA, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para BRFS3.SA, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para GIS, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para HRL, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para JBSS3.SA, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para MRFG3.SA, pois 'Adj Close' não está disponível.
⚠️ Usando 'Close' para TSN, pois 'Adj Close' não está disponível.
retornos_diarios = precos.pct_change().dropna()
if retornos_diarios.empty:
raise ValueError("❌ Retornos diários insuficientes.")
retorno_medio_anual = (retornos_diarios.mean() * 252) * 100
risco_anual = (retornos_diarios.std() * np.sqrt(252)) * 100
df = pd.DataFrame({
'Ticker': retorno_medio_anual.index,
'Retorno Esperado (%)': retorno_medio_anual.values,
'Risco (%)': risco_anual.values
})
df| Ticker | Retorno Esperado (%) | Risco (%) | |
|---|---|---|---|
| 0 | BEEF3.SA | 19.952812 | 52.777333 |
| 1 | BRFS3.SA | -5.995730 | 38.618763 |
| 2 | GIS | -44.667148 | 24.262770 |
| 3 | HRL | -1.237448 | 21.940254 |
| 4 | JBSS3.SA | 82.872586 | 41.321218 |
| 5 | MRFG3.SA | 121.350365 | 47.075397 |
| 6 | TSN | 10.791442 | 22.756918 |
df_normalized = df.copy()
scaler_ret = MinMaxScaler()
df_normalized['Retorno Normalizado'] = scaler_ret.fit_transform(df[['Retorno Esperado (%)']])
scaler_risk = MinMaxScaler()
df_normalized['Risco Normalizado'] = 1 - scaler_risk.fit_transform(df[['Risco (%)']])
peso_retorno = 0.6
peso_risco = 0.4
df_normalized['Retorno Ponderado'] = df_normalized['Retorno Normalizado'] * peso_retorno
df_normalized['Risco Ponderado'] = df_normalized['Risco Normalizado'] * peso_riscoideal_positivo = [
df_normalized['Retorno Ponderado'].max(),
df_normalized['Risco Ponderado'].max()
]
ideal_negativo = [
df_normalized['Retorno Ponderado'].min(),
df_normalized['Risco Ponderado'].min()
]
distancia_positiva = np.sqrt(
(df_normalized['Retorno Ponderado'] - ideal_positivo[0])**2 +
(df_normalized['Risco Ponderado'] - ideal_positivo[1])**2
)
distancia_negativa = np.sqrt(
(df_normalized['Retorno Ponderado'] - ideal_negativo[0])**2 +
(df_normalized['Risco Ponderado'] - ideal_negativo[1])**2
)
df_normalized['Índice Similaridade'] = distancia_negativa / (distancia_positiva + distancia_negativa)
df_normalized['Rank'] = df_normalized['Índice Similaridade'].rank(ascending=False)| Ticker | Retorno Esperado (%) | Risco (%) | Índice Similaridade | Rank | |
|---|---|---|---|---|---|
| 5 | MRFG3.SA | 121.350365 | 47.075397 | 0.649640 | 1.0 |
| 4 | JBSS3.SA | 82.872586 | 41.321218 | 0.627660 | 2.0 |
| 6 | TSN | 10.791442 | 22.756918 | 0.522833 | 3.0 |
| 3 | HRL | -1.237448 | 21.940254 | 0.492352 | 4.0 |
| 2 | GIS | -44.667148 | 24.262770 | 0.381066 | 5.0 |
| 1 | BRFS3.SA | -5.995730 | 38.618763 | 0.312154 | 6.0 |
| 0 | BEEF3.SA | 19.952812 | 52.777333 | 0.300945 | 7.0 |